package de.rj.urwalking.data;

import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.List;

import org.poly2tri.triangulation.delaunay.DelaunayTriangle;

import com.threed.jpct.Loader;
import com.threed.jpct.Object3D;
import com.threed.jpct.SimpleVector;

import de.rj.urwalking.config.Config;
import de.rj.urwalking.ui.OutdoorRenderer;

/**
 * 
 * A sublass of {@link SalientObject} that represents a building for outdoor navigation. Contains methods for building
 * the 3d model from a list of coordinates.
 * 
 * @author rjj
 * 
 */

public class Building extends SalientObject {
	private static final long serialVersionUID = 1L;

	public static final int OPACITY_LOW = 5;
	public static final int OPACITY_FULL = 20;
	

	private int gebid;
	private double hoehegeb;
	private String lagehnr;
	private DoubleVector centroid;
	private ArrayList<SimpleVector> polygonPoints;


	public Building(int gebid, double hoehegeb, String lagehnr, ArrayList<SimpleVector> polygonPoints,
			DoubleVector centroid, List<DelaunayTriangle> roofTriangles) {

		// call the super constructor to create an Object3D object using JPCTs built-in Loader class
		super(Loader.loadOBJ(parseToObj(polygonPoints, gebid, hoehegeb, roofTriangles), null, 1)[0]);

		this.gebid = gebid;
		this.hoehegeb = hoehegeb;
		this.lagehnr = lagehnr;
		this.centroid = centroid;
		this.polygonPoints = polygonPoints;

		setName(String.valueOf(gebid));

		setAdditionalColor(OutdoorRenderer.BUILDING_COLOR);
		setCulling(false);
		setCollisionMode(Object3D.COLLISION_CHECK_OTHERS);		
		scale(Config.BUILDING_SCALE_FACTOR);

		strip();
		build();

	}

	public DoubleVector getCentroid() {
		return centroid;
	}

	public int getGebid() {
		return gebid;
	}

	public String getLagehnr() {
		return lagehnr;
	}

	/**
	 * Checks if the Polygon contains a point.
	 * 
	 * @see "http://alienryderflex.com/polygon/"
	 * @param x
	 *            Point horizontal pos.
	 * @param y
	 *            Point vertical pos.
	 * @return true if coordinates are in this building, false if not
	 */
	public boolean contains(double x, double y) {

		boolean oddTransitions = false;
		for (int i = 0, j = polygonPoints.size() - 1; i < polygonPoints.size(); j = i++) {
			if ((polygonPoints.get(i).y < y && polygonPoints.get(j).y >= y)
					|| (polygonPoints.get(j).y < y && polygonPoints.get(i).y >= y)) {

				if (polygonPoints.get(i).x + (y - polygonPoints.get(i).y)
						/ (polygonPoints.get(j).y - polygonPoints.get(i).y)
						* (polygonPoints.get(j).x - polygonPoints.get(i).x) < x) {

					oddTransitions = !oddTransitions;
				}
			}
		}
		return oddTransitions;
	}

	public void dontShowAsLandmark() {

		clearAdditionalColor();
		setAdditionalColor(OutdoorRenderer.BUILDING_COLOR);

	}

	/**
	 * takes the polygon as ArrayList of SimpleVectors and builds a String in Wavefront OBJ format
	 * 
	 * @param objectCoords
	 * @param gebid
	 * @param roofTriangles
	 * @param triangles
	 * @return an InputStream
	 */

	private static InputStream parseToObj(ArrayList<SimpleVector> objectCoords, int gebid, double hoehegeb,
			List<DelaunayTriangle> roofTriangles) {

		StringBuilder objString = new StringBuilder();

		objString.append("o " + gebid + "\n");

		if (Config.DEBUG) {
			objString.append("# vertices for walls \n");
		}
		// vertices for walls
		for (SimpleVector vector : objectCoords) {

			// add two vertices, one on the ground and one up in the air
			objString.append("v " + vector.x + " " + vector.y + " 0");
			objString.append('\n');
			objString.append("v " + vector.x + " " + vector.y + " " + vector.z);
			objString.append('\n');

		}

		if (roofTriangles != null) {
			if (Config.DEBUG) {
				objString.append("# vertices for roof \n");
			}

			// vertices for roof
			for (DelaunayTriangle triangle : roofTriangles) {

				objString.append("v " + triangle.points[0].getX() + " " + triangle.points[0].getY() + " " + hoehegeb);
				objString.append('\n');
				objString.append("v " + triangle.points[1].getX() + " " + triangle.points[1].getY() + " " + hoehegeb);
				objString.append('\n');
				objString.append("v " + triangle.points[2].getX() + " " + triangle.points[2].getY() + " " + hoehegeb);
				objString.append('\n');
			}
		}
		if (Config.DEBUG) {
			objString.append("# faces for walls \n");
		}
		// define faces consisting of 4 vertices
		for (int i = 1; i < (objectCoords.size() * 2) - 2; i += 2) {

			objString.append("f " + i + " " + (i + 2) + " " + (i + 3) + " " + (i + 1));
			objString.append('\n');

		}

		// add the last face using the last and first two vertices
		objString.append("f " + (objectCoords.size() * 2 - 1) + " 1 2 " + objectCoords.size() * 2);
		objString.append('\n');

		if (roofTriangles != null) {
			int triangleVertexStartIndex = objectCoords.size() * 2;

			if (Config.DEBUG) {
				objString.append("# faces for roof \n");
			}

			// add faces for roof
			for (int i = triangleVertexStartIndex; i < triangleVertexStartIndex + (roofTriangles.size() * 3); i += 3) {
				objString.append("f " + (i + 1) + " " + (i + 2) + " " + (i + 3));
				objString.append('\n');
			}
		}
		// wrap String in an InputStream to be able to use built-in JPCT Loader class
		String obj = objString.toString();
		InputStream is = null;
		try {
			is = new ByteArrayInputStream(obj.getBytes("UTF-8"));
		} catch (UnsupportedEncodingException e) {
			e.printStackTrace();
		}

		return is;

	}

}
